package org.springcloud.plugin.strategy.service.interceptor;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springcloud.plugin.common.constant.DiscoveryConstant;
import org.springcloud.plugin.framework.strategy.constant.StrategyConstant;
import org.springcloud.plugin.strategy.service.adapter.FeignStrategyInterceptorAdapter;
import org.springcloud.plugin.strategy.service.filter.ServiceStrategyRouteFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;

/**
 * @Author yanggld
 * @Date 2020/2/5-17:24
 * @Description
 */
public class FeignStrategyInterceptor extends AbstractStrategyInterceptor implements RequestInterceptor {
	private static final Logger LOG = LoggerFactory.getLogger(FeignStrategyInterceptor.class);

	@Autowired(required = false)
	private List<FeignStrategyInterceptorAdapter> feignStrategyInterceptorAdapterList;

	@Autowired
	private ServiceStrategyRouteFilter serviceStrategyRouteFilter;

	@Value("${" + StrategyConstant.SPRING_APPLICATION_STRATEGY_TRACE_ENABLED + ":false}")
	protected Boolean strategyTraceEnabled;

	public FeignStrategyInterceptor(String contextRequestHeaders, String businessRequestHeaders) {
		super(contextRequestHeaders, businessRequestHeaders);

		LOG.info("----------- Feign Intercept Information ----------");
		LOG.info("Feign desires to intercept customer headers are {}", requestHeaderList);
		LOG.info("--------------------------------------------------");
	}

	@Override
	public void apply(RequestTemplate requestTemplate) {
		interceptInputHeader();

		applyInnerHeader(requestTemplate);
		applyOuterHeader(requestTemplate);

		if (CollectionUtils.isNotEmpty(feignStrategyInterceptorAdapterList)) {
			for (FeignStrategyInterceptorAdapter feignStrategyInterceptorAdapter : feignStrategyInterceptorAdapterList) {
				feignStrategyInterceptorAdapter.apply(requestTemplate);
			}
		}

		interceptOutputHeader(requestTemplate);
	}

	private void applyInnerHeader(RequestTemplate requestTemplate) {
		requestTemplate.header(DiscoveryConstant.N_D_SERVICE_GROUP, coreAdapter.getGroup());
		if (strategyTraceEnabled) {
			requestTemplate.header(DiscoveryConstant.N_D_SERVICE_ID, coreAdapter.getServiceId());
			requestTemplate.header(DiscoveryConstant.N_D_SERVICE_ADDRESS, coreAdapter.getHost() + ":" + coreAdapter.getPort());
			requestTemplate.header(DiscoveryConstant.N_D_SERVICE_VERSION, coreAdapter.getVersion());
			requestTemplate.header(DiscoveryConstant.N_D_SERVICE_REGION, coreAdapter.getRegion());
			requestTemplate.header(DiscoveryConstant.N_D_SERVICE_ENVIRONMENT, coreAdapter.getEnvironment());
		}
	}

	private void applyOuterHeader(RequestTemplate requestTemplate) {
		ServletRequestAttributes attributes = serviceStrategyContextHolder.getRestAttributes();
		if (attributes == null) {
			return;
		}

		HttpServletRequest previousRequest = attributes.getRequest();
		Enumeration<String> headerNames = previousRequest.getHeaderNames();
		if (headerNames == null) {
			return;
		}

		while (headerNames.hasMoreElements()) {
			String headerName = headerNames.nextElement();
			String headerValue = previousRequest.getHeader(headerName);
			boolean isHeaderContains = isHeaderContainsExcludeInner(headerName.toLowerCase());
			if (isHeaderContains) {
				requestTemplate.header(headerName, headerValue);
			}
		}

		Map<String, Collection<String>> headers = requestTemplate.headers();
		if (CollectionUtils.isEmpty(headers.get(DiscoveryConstant.N_D_VERSION))) {
			String routeVersion = serviceStrategyRouteFilter.getRouteVersion();
			if (StringUtils.isNotEmpty(routeVersion)) {
				requestTemplate.header(DiscoveryConstant.N_D_VERSION, routeVersion);
			}
		}
		if (CollectionUtils.isEmpty(headers.get(DiscoveryConstant.N_D_REGION))) {
			String routeRegion = serviceStrategyRouteFilter.getRouteRegion();
			if (StringUtils.isNotEmpty(routeRegion)) {
				requestTemplate.header(DiscoveryConstant.N_D_REGION, routeRegion);
			}
		}
		if (CollectionUtils.isEmpty(headers.get(DiscoveryConstant.N_D_VERSION_WEIGHT))) {
			String routeVersionWeight = serviceStrategyRouteFilter.getRouteVersionWeight();
			if (StringUtils.isNotEmpty(routeVersionWeight)) {
				requestTemplate.header(DiscoveryConstant.N_D_VERSION_WEIGHT, routeVersionWeight);
			}
		}
		if (CollectionUtils.isEmpty(headers.get(DiscoveryConstant.N_D_REGION_WEIGHT))) {
			String routeRegionWeight = serviceStrategyRouteFilter.getRouteRegionWeight();
			if (StringUtils.isNotEmpty(routeRegionWeight)) {
				requestTemplate.header(DiscoveryConstant.N_D_REGION_WEIGHT, routeRegionWeight);
			}
		}
	}

	private void interceptOutputHeader(RequestTemplate requestTemplate) {
		if (!interceptDebugEnabled) {
			return;
		}

		System.out.println("------- Intercept Output Header Information ------");
		Map<String, Collection<String>> headers = requestTemplate.headers();
		for (Map.Entry<String, Collection<String>> entry : headers.entrySet()) {
			String headerName = entry.getKey();
			boolean isHeaderContains = isHeaderContains(headerName.toLowerCase());
			if (isHeaderContains) {
				Collection<String> headerValue = entry.getValue();

				System.out.println(headerName + "=" + headerValue);
			}
		}
		System.out.println("--------------------------------------------------");
	}
}